package bbcom.modeling;

import bbcom.*;
import bbcom.messages.BBCoMMessageResource;
import bbcom.messages.BBCoMMessageUtility;
import bbcom.messages.BBCoMModelingException;
import org.apache.xmlbeans.*;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Thanh-Trung Pham.
 */
public class BBCoMModeling {
    private static final String m_namespaceDeclaration = "declare namespace tns='bbcom';";

    private String m_strBBCoMInstanceFilename;
    private boolean m_isValid = false;

    private boolean m_isBBComInstanceSet = false;
    private List<XmlError> m_oXMLErrors;
    private XmlObject m_oXmlObject;

    private boolean m_isQuickValidation = false;

    /**
     * The constructor.
     */
    public BBCoMModeling() {

    }

    /**
     * Validate the BBCoM instance.
     *
     * @throws BBCoMModelingException if there is an error.
     */
    private void validateBBCoMInstance() throws BBCoMModelingException {
        m_oXMLErrors = new ArrayList<>();

        XmlOptions oXMLOptions = new XmlOptions();
        oXMLOptions.setLoadLineNumbers(XmlOptions.LOAD_LINE_NUMBERS);

        try {
            m_oXmlObject = XmlObject.Factory.parse(new File(m_strBBCoMInstanceFilename), oXMLOptions);
        } catch (Exception oException) {
            //TODO: log into log files
            oException.printStackTrace();

            throw new BBCoMModelingException(BBCoMMessageUtility.getResourceMessage(BBCoMMessageResource.BBCOMMODELING_UNEXPECTED_EXCEPTION, oException.getMessage()));
        }

        oXMLOptions.setErrorListener(m_oXMLErrors);
        if (m_oXmlObject.validate(oXMLOptions)) {
            if (!m_isQuickValidation) {
                //TODO: More validations
            }
        } else {
            m_oXmlObject = null;
            throw new BBCoMModelingException(BBCoMMessageUtility.getResourceMessage(BBCoMMessageResource.BBCOMMODELING_INVALID_BBCOMINSTANCE));
        }
    }

    /**
     * Get all the XML errors.
     *
     * @return all the XML errors.
     */
    public List<XmlError> getXMLErrors() {
        return m_oXMLErrors;
    }

    /**
     * Check if the BBComInstance is valid.
     *
     * @return true if the BBComInstance is valid. Otherwise, false.
     */
    public boolean isValid() {
        return m_isValid;
    }

    /**
     * Get the BBCoM instance filename.
     *
     * @return the BBCoM instance filename.
     */
    public String getBBCoMInstanceFilename() {
        return new File(m_strBBCoMInstanceFilename).getName();
    }

    /**
     * Set the BBCoM instance filename.
     *
     * @param BBCoMInstanceFilename  the BBCoM instance filename.
     * @throws BBCoMModelingException if there is an error.
     */
    public void setBBCoMInstanceFilename(String BBCoMInstanceFilename) throws BBCoMModelingException {
        if (m_isBBComInstanceSet) {
            throw new BBCoMModelingException(BBCoMMessageUtility.getResourceMessage(BBCoMMessageResource.BBCOMMODELING_INVALID_REUSING));
        }

        m_isBBComInstanceSet = true;
        m_strBBCoMInstanceFilename = BBCoMInstanceFilename;

        try {
            validateBBCoMInstance();
            m_isValid = true;
        } catch (BBCoMModelingException oBBCoMModelingException) {
            m_isValid = false;
            throw oBBCoMModelingException;
        }
    }

    /**
     * Get the deployment.
     *
     * @return the deployment.
     */
    public DeploymentType getDeployment() {
        String strXPath = m_namespaceDeclaration + "$this/tns:BBCoMInstance/tns:Deployment";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return ((DeploymentType) oXmlObjects[0]);
    }

    /**
     * Get the system by the given system name.
     *
     * @param systemName the given system name.
     * @return the system.
     */
    public SystemType getSystem(String systemName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:BBCoMInstance/tns:System[@name='" + systemName + "']";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return ((SystemType) oXmlObjects[0]);
    }

    /**
     * Get the system by the given deployment.
     *
     * @param deployment the given deployment
     * @return the system.
     */
    public SystemType getSystem(DeploymentType deployment) {
        String strRefSystem = deployment.getRefSystem();
        return getSystem(strRefSystem);
    }

    /**
     * Get the device environment by the given device environment name.
     *
     * @param deviceEnvironmentName the given device environment name.
     * @return the device environment
     */
    public DeviceEnvironmentType getDeviceEnvironment(String deviceEnvironmentName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:BBCoMInstance/tns:DeviceEnvironment[@name='" + deviceEnvironmentName + "']";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return ((DeviceEnvironmentType) oXmlObjects[0]);
    }

    /**
     * Get the device environment by the given deployment.
     *
     * @param deployment the given deployment.
     * @return the device environment.
     */
    public DeviceEnvironmentType getDeviceEnvironment(DeploymentType deployment) {
        String strRefDeviceEnvironment = deployment.getRefDeviceEnvironment();
        return getDeviceEnvironment(strRefDeviceEnvironment);
    }

    /**
     * Get the device container by the given device environment and the given device container name.
     *
     * @param deviceEnvironment   the given device environment.
     * @param deviceContainerName the given device container name.
     * @return the device container.
     */
    public DeviceContainerType getDeviceContainer(DeviceEnvironmentType deviceEnvironment, String deviceContainerName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:DeviceContainer[@name='" + deviceContainerName + "']";


        XmlObject[] oXmlObjects = deviceEnvironment.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return ((DeviceContainerType) oXmlObjects[0]);
    }

    /**
     * Get all device containers of the given device environment.
     *
     * @param deviceEnvironment the given device environment.
     * @return all device containers.
     */
    public DeviceContainerType[] getAllDeviceContainers(DeviceEnvironmentType deviceEnvironment) {
        return deviceEnvironment.getDeviceContainerArray();

    }

    /**
     * Get the list of allocation names by the given deployment.
     *
     * @param deployment the deployment.
     * @return the list of allocation names.
     */
    public List<String> getAllocationNames(DeploymentType deployment) {
        List<String> oAllocationNames = new ArrayList<>();

        AllocationType[] oAllocations = deployment.getAllocationArray();
        for (AllocationType oAllocation : oAllocations) {
            String strAllocationName = oAllocation.getName();
            oAllocationNames.add(strAllocationName);
        }

        return oAllocationNames;
    }

    /**
     * Get the allocations by given deployment and the given referenced device container name.
     *
     * @param deployment             the given deployment.
     * @param refDeviceContainerName the given referenced device container name.
     * @return the allocations.
     */
    private AllocationType[] getAllocations(DeploymentType deployment, String refDeviceContainerName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:Allocation[@refDeviceContainer='" + refDeviceContainerName + "']";
        XmlObject[] oXmlObjects = deployment.selectPath(strXPath);

        return (AllocationType[]) oXmlObjects;
    }

    /**
     * Get the allocation by the given deployment and the given allocation name.
     *
     * @param deployment     the given deployment.
     * @param allocationName the given allocation name.
     * @return the allocation.
     */
    public AllocationType getAllocation(DeploymentType deployment, String allocationName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:Allocation[@name='" + allocationName + "']";
        XmlObject[] oXmlObjects = deployment.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return (AllocationType) oXmlObjects[0];
    }


    /**
     * Get the device container referenced by an allocation in the given deployment.
     *
     * @param deployment     the given deployment.
     * @param allocationName the given allocation's name.
     * @return the device container.
     */
    public DeviceContainerType getDeviceContainerReferencedByAllocation(DeploymentType deployment, String allocationName) {
        AllocationType oAllocation = getAllocation(deployment, allocationName);
        String strRefDeviceContainer = oAllocation.getRefDeviceContainer();

        DeviceEnvironmentType oDeviceEnvironment = getDeviceEnvironment(deployment);
        return getDeviceContainer(oDeviceEnvironment, strRefDeviceContainer);
    }


    /**
     * Get the deployable entity referenced by an allocation in the given deployment.
     *
     * @param deployment     the given deployment
     * @param allocationName the given allocation's name.
     * @return the deployable entity.
     */
    public DeployableEntityType getDeployableEntityReferencedByAllocation(DeploymentType deployment, String allocationName) {
        AllocationType oAllocation = getAllocation(deployment, allocationName);
        String strRefDeployableEntity = oAllocation.getRefDeployableEntity();

        SystemType oSystem = getSystem(deployment);

        EventChannelType oEventChannel = getEventChannel(oSystem, strRefDeployableEntity);
        if (oEventChannel != null) {
            return oEventChannel;
        }

        ComponentInstanceType oComponentInstance = getComponentInstance(oSystem, strRefDeployableEntity);
        return oComponentInstance;
    }

    /**
     * Get the deployable entity referenced by an allocation (in the given deployment) which references to the given device container.
     *
     * @param deployment          the given deployment.
     * @param allocationName      the given allocation's name.
     * @param deviceContainerName the given device container's name.
     * @return the deployable entity.
     */
    public DeployableEntityType getDeployableEntityReferencedByAllocation(DeploymentType deployment, String allocationName, String deviceContainerName) {
        AllocationType oAllocation = getAllocation(deployment, allocationName);
        if (oAllocation.getRefDeviceContainer().equals(deviceContainerName)) {
            return getDeployableEntityReferencedByAllocation(deployment, allocationName);
        }

        return null;
    }


    /**
     * Get the event channel by the given event channel name in the given system.
     *
     * @param system           the given system.
     * @param eventChannelName the given event channel name.
     * @return the event channel.
     */
    public EventChannelType getEventChannel(SystemType system, String eventChannelName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:EventChannel[@name='" + eventChannelName + "']";

        XmlObject[] oXmlObjects = system.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return ((EventChannelType) oXmlObjects[0]);
    }

    /**
     * Get the component instance by the given component instance name in the given system.
     *
     * @param system                the given system.
     * @param componentInstanceName the given component instance name.
     * @return the component instance.
     */
    public ComponentInstanceType getComponentInstance(SystemType system, String componentInstanceName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:ComponentInstance[@name='" + componentInstanceName + "']";


        XmlObject[] oXmlObjects = system.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return ((ComponentInstanceType) oXmlObjects[0]);
    }

    /**
     * Get all the connectors of the given system
     *
     * @param system the given system.
     * @return the connectors.
     */
    public ConnectorType[] getConnectors(SystemType system) {
        return system.getConnectorArray();
    }

    /**
     * Get all the event connectors of the given system.
     *
     * @param system the given system.
     * @return all the event connectors.
     */
    public EventConnectorType[] getEventConnectors(SystemType system) {
        return system.getEventConnectorArray();
    }

    /**
     * Get all the channel source connectors of the given system.
     *
     * @param system the given system.
     * @return all the channel source connectors.
     */
    public ChannelSourceConnectorType[] getChannelSourceConnectors(SystemType system) {
        return system.getChannelSourceConnectorArray();
    }

    /**
     * Get all the channel sink connectors of the given system.
     *
     * @param system the given system.
     * @return all the channel sink connectors.
     */
    public ChannelSinkConnectorType[] getChannelSinkConnectors(SystemType system) {
        return system.getChannelSinkConnectorArray();
    }

    /**
     * Get the channel sink connectors by the given event channel name within the given system.
     *
     * @param system           the given system.
     * @param eventChannelName the given event channel name.
     * @return the channel sink connectors
     */
    public ChannelSinkConnectorType[] getChannelSinkConnector(SystemType system, String eventChannelName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:ChannelSinkConnector[@refEventChannel='" + eventChannelName + "']";

        XmlObject[] oXmlObjects = system.selectPath(strXPath);
        return (ChannelSinkConnectorType[]) oXmlObjects;
    }

    /**
     * Get the implementation of the given filter rule.
     *
     * @param filterRule the given filter rule.
     * @return the implementation
     */
    public ImplementationType getImplementation(FilterRuleType filterRule) {
        if(filterRule == null){
            return null;
        }

        return filterRule.getImplementation();
    }

    /**
     * Get the implementation of the given interface.
     *
     * @param intf the given interface
     * @return the implementation.
     */
    public ImplementationType getImplementation(InterfaceType intf) {
        return intf.getImplementation();
    }

    /**
     * Get the implementation of the given event group.
     *
     * @param eventGroup the given event group.
     * @return the implementation
     */
    public ImplementationType getImplementation(EventGroupType eventGroup) {
        return eventGroup.getImplementation();
    }

    /**
     * Get the list of implementations by the given primitive component.
     *
     * @param primitiveComponent the given primitive component.
     * @return the list of implementations.
     */
    public List<ImplementationType> getImplementations(PrimitiveComponentType primitiveComponent) {
        List<ImplementationType> oReturnImplementations = new ArrayList<>();
        for (ProvidedRoleType oProvidedRole : primitiveComponent.getProvidedRoleArray()) {
            addImplementation(oReturnImplementations, getImplementation(getInterface(oProvidedRole.getRefInterface())));
        }

        for (RequiredRoleType oRequiredRole : primitiveComponent.getRequiredRoleArray()) {
            addImplementation(oReturnImplementations, getImplementation(getInterface(oRequiredRole.getRefInterface())));
        }

        for (SourceRoleType oSourceRole : primitiveComponent.getSourceRoleArray()) {
            addImplementation(oReturnImplementations, getImplementation(getEventGroup(oSourceRole.getRefEventGroup())));
        }

        for (SinkRoleType oSinkRole : primitiveComponent.getSinkRoleArray()) {
            addImplementation(oReturnImplementations, getImplementation(getEventGroup(oSinkRole.getRefEventGroup())));
        }

        addImplementation(oReturnImplementations, primitiveComponent.getImplementation());

        return oReturnImplementations;
    }


    /**
     * Get the list of implementations for the given system.
     *
     * @param system the given system.
     * @return the list of implementations
     */
    public List<ImplementationType> getImplementations(SystemType system) {
        if (system == null) {
            return null;
        }

        List<ImplementationType> oReturnImpls = new ArrayList<>();

        for (EventChannelType oEventChannel : system.getEventChannelArray()) {
            List<ImplementationType> oImpls = getImplementations(oEventChannel);
            addImplementations(oReturnImpls, oImpls);
        }

        for (ComponentInstanceType oComponentInstance : system.getComponentInstanceArray()) {
            List<ImplementationType> oImpls = getImplementations(oComponentInstance);
            addImplementations(oReturnImpls, oImpls);
        }

        return oReturnImpls;

    }

    /**
     * Get the list of implementations for the given component instance.
     *
     * @param componentInstance the given component instance.
     * @return the list of implementations
     */
    public List<ImplementationType> getImplementations(ComponentInstanceType componentInstance) {
        ComponentType oComponent = getComponent(componentInstance);
        if (oComponent instanceof PrimitiveComponentType) {
            return getImplementations((PrimitiveComponentType) oComponent);
        } else {
            return null;
        }
    }


    /**
     * Get the list of implementations for the given event channel.
     *
     * @param eventChannel the given event channel.
     * @return the list of implementations
     */
    public List<ImplementationType> getImplementations(EventChannelType eventChannel) {
        List<ImplementationType> oReturnImplementations = new ArrayList<>();
        addImplementation(oReturnImplementations, getImplementation(getEventGroup(eventChannel.getRefEventGroup())));

        for (ChannelSinkConnectorType oChannelSinkConnector : getChannelSinkConnector((SystemType) getParentXmlObject(eventChannel), eventChannel.getName())) {
            addImplementation(oReturnImplementations, getImplementation(oChannelSinkConnector.getFilterRule()));
        }

        return oReturnImplementations;
    }

    /**
     * Add the given implementation to the given target list of implementations.
     *
     * @param targetListOfImplementations the given target list of implementations.
     * @param implementation              the given implementation
     */
    private void addImplementation(List<ImplementationType> targetListOfImplementations, ImplementationType implementation) {
        if (implementation != null && !targetListOfImplementations.contains(implementation)) {
            targetListOfImplementations.add(implementation);
        }
    }

    /**
     * Add the given implementations to the given target list of implementations.
     *
     * @param targetListOfImplementations the given target list of implementations.
     * @param implementations             the given implementations
     */
    private void addImplementations(List<ImplementationType> targetListOfImplementations, List<ImplementationType> implementations) {
        if (implementations == null) {
            return;
        }

        for (ImplementationType implementation : implementations) {
            addImplementation(targetListOfImplementations, implementation);
        }
    }

    /**
     * Add the given interface to the given target list of interfaces.
     *
     * @param targetListOfInterfaces the given target list of interfaces.
     * @param intf                   the given interface
     */
    private void addInterface(List<InterfaceType> targetListOfInterfaces, InterfaceType intf) {
        if (intf != null && !targetListOfInterfaces.contains(intf)) {
            targetListOfInterfaces.add(intf);
        }
    }

    /**
     * Add the given interfaces to the given target list of interfaces.
     *
     * @param targetListOfInterfaces the given target list of interfaces.
     * @param interfaces             the given interface.
     */
    private void addInterfaces(List<InterfaceType> targetListOfInterfaces, List<InterfaceType> interfaces) {
        if (interfaces == null) {
            return;
        }

        for (InterfaceType oInterface : interfaces) {
            addInterface(targetListOfInterfaces, oInterface);
        }
    }

    /**
     * Add the given event group to the given target list of event groups.
     *
     * @param targetListOfEventGroups the given target list of event groups.
     * @param eventGroup              the given event group.
     */
    private void addEventGroup(List<EventGroupType> targetListOfEventGroups, EventGroupType eventGroup) {
        if (eventGroup != null && !targetListOfEventGroups.contains(eventGroup)) {
            targetListOfEventGroups.add(eventGroup);
        }
    }

    /**
     * Add the given event groups to the given target list of event groups.
     *
     * @param targetListOfEventGroups the given target list of event groups.
     * @param eventGroups             the given event groups.
     */
    private void addEventGroups(List<EventGroupType> targetListOfEventGroups, List<EventGroupType> eventGroups) {
        if (eventGroups == null) {
            return;
        }

        for (EventGroupType oEventGroup : eventGroups) {
            addEventGroup(targetListOfEventGroups, oEventGroup);
        }
    }


    /**
     * Get the list of implementations of the given deployable entity.
     *
     * @param deployableEntity the given deployable entity.
     * @return the list of implementations.
     */
    public List<ImplementationType> getImplementations(DeployableEntityType deployableEntity) {
        if (deployableEntity instanceof ComponentInstanceType) {
            return getImplementations((ComponentInstanceType) deployableEntity);
        } else {
            return getImplementations((EventChannelType) deployableEntity);
        }
    }

    /**
     * Get the parent XmlObject of the given XmlObject.
     *
     * @param xmlObject the given XmlObject
     * @return the parent XmlObject.
     */
    private XmlObject getParentXmlObject(XmlObject xmlObject) {
        XmlCursor oXmlCursor = xmlObject.newCursor();
        if (!oXmlCursor.toParent()) {
            return null;
        }


        return oXmlCursor.getObject();
    }

    /**
     * Get the primitive component by the given primitive component name.
     *
     * @param primitiveComponentName the given primitive component name.
     * @return the primitive component
     */
    private PrimitiveComponentType getPrimitiveComponent(String primitiveComponentName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:BBCoMInstance/tns:Depository/tns:PrimitiveComponent[@name='" + primitiveComponentName + "']";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return ((PrimitiveComponentType) oXmlObjects[0]);
    }

    /**
     * Get the component by the given component name.
     *
     * @param componentName the given component name.
     * @return the component.
     */
    public ComponentType getComponent(String componentName) {
        PrimitiveComponentType oPrimitiveComponent = getPrimitiveComponent(componentName);
        if (oPrimitiveComponent != null) {
            return oPrimitiveComponent;
        }

        return null;
    }


    /**
     * Get the component by the given component instance.
     *
     * @param componentInstance the given component instance.
     * @return the component.
     */
    public ComponentType getComponent(ComponentInstanceType componentInstance) {
        return getComponent(componentInstance.getRefComponent());
    }


    /**
     * Get the source role (of the given component) by its name.
     *
     * @param component      the given component.
     * @param sourceRoleName the source role's name.
     * @return the source role.
     */
    public SourceRoleType getSourceRole(ComponentType component, String sourceRoleName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:SourceRole[@name='" + sourceRoleName + "']";


        XmlObject[] oXmlObjects = component.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return ((SourceRoleType) oXmlObjects[0]);
    }

    /**
     * Get the sink role (of the given component) by its name.
     *
     * @param component    the given component.
     * @param sinkRoleName the sink role's name.
     * @return the sink role.
     */
    public SinkRoleType getSinkRole(ComponentType component, String sinkRoleName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:SinkRole[@name='" + sinkRoleName + "']";


        XmlObject[] oXmlObjects = component.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return ((SinkRoleType) oXmlObjects[0]);
    }

    /**
     * Get the required role (of the given component) by its name.
     *
     * @param component        the given component.
     * @param requiredRoleName the required role's name.
     * @return the required role.
     */
    public RequiredRoleType getRequiredRole(ComponentType component, String requiredRoleName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:RequiredRole[@name='" + requiredRoleName + "']";


        XmlObject[] oXmlObjects = component.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return ((RequiredRoleType) oXmlObjects[0]);
    }

    /**
     * Get the provided role (of the given component) by its name.
     *
     * @param component        the given component.
     * @param providedRoleName the the provided role's name.
     * @return the provided role.
     */
    public ProvidedRoleType getProvidedRole(ComponentType component, String providedRoleName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:ProvidedRole[@name='" + providedRoleName + "']";


        XmlObject[] oXmlObjects = component.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return ((ProvidedRoleType) oXmlObjects[0]);
    }

    /**
     * Get the event group referenced by a role.
     *
     * @param role the given role.
     * @return the event group.
     */
    public EventGroupType getEventGroupReferencedByRole(RoleType role) {
        XmlObject oParentComponent = getParentXmlObject(role);
        if (oParentComponent instanceof PrimitiveComponentType) {
            if (role instanceof SinkRoleType) {
                String strRefEventGroup = ((SinkRoleType) role).getRefEventGroup();
                return getEventGroup(strRefEventGroup);
            }

            if (role instanceof SourceRoleType) {
                String strRefEventGroup = ((SourceRoleType) role).getRefEventGroup();
                return getEventGroup(strRefEventGroup);
            }

            return null;
        } else {
            return null;
        }
    }

    /**
     * Get the interface referenced by a role.
     *
     * @param role the given role.
     * @return the interface.
     */
    public InterfaceType getInterfaceReferencedByRole(RoleType role) {
        XmlObject oParentComponent = getParentXmlObject(role);
        if (oParentComponent instanceof PrimitiveComponentType) {
            if (role instanceof ProvidedRoleType) {
                String strRefInterface = ((ProvidedRoleType) role).getRefInterface();
                return getInterface(strRefInterface);
            }

            if (role instanceof RequiredRoleType) {
                String strRefInterface = ((RequiredRoleType) role).getRefInterface();
                return getInterface(strRefInterface);
            }

            return null;
        } else {
            return null;
        }

    }


    /**
     * Get the interface by the given interface name.
     *
     * @param interfaceName the given interface name.
     * @return the interface.
     */
    public InterfaceType getInterface(String interfaceName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:BBCoMInstance/tns:Depository/tns:Interface[@name='" + interfaceName + "']";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return ((InterfaceType) oXmlObjects[0]);
    }

    /**
     * Get the list of interfaces of the given deployable entity.
     *
     * @param deployableEntity the given deployable entity.
     * @return the list of interface.
     */
    public List<InterfaceType> getInterfaces(DeployableEntityType deployableEntity) {
        if (deployableEntity instanceof ComponentInstanceType) {
            return getInterfaces((ComponentInstanceType) deployableEntity);
        } else {
            return null;
        }
    }

    /**
     * Get the list of interfaces by the given component instance.
     *
     * @param componentInstance the given component instance.
     * @return the list of interfaces
     */
    public List<InterfaceType> getInterfaces(ComponentInstanceType componentInstance) {
        ComponentType oComponent = getComponent(componentInstance);
        if (oComponent instanceof PrimitiveComponentType) {
            return getInterfaces((PrimitiveComponentType) oComponent);
        } else {
            return null;
        }
    }

    /**
     * Get the list of interfaces by the given primitive component.
     *
     * @param primitiveComponent the given primitive component.
     * @return the list of interfaces.
     */
    public List<InterfaceType> getInterfaces(PrimitiveComponentType primitiveComponent) {
        List<InterfaceType> oReturnInterfaces = new ArrayList<>();
        for (ProvidedRoleType oProvidedRole : primitiveComponent.getProvidedRoleArray()) {
            addInterface(oReturnInterfaces, getInterface(oProvidedRole.getRefInterface()));
        }

        for (RequiredRoleType oRequiredRole : primitiveComponent.getRequiredRoleArray()) {
            addInterface(oReturnInterfaces, getInterface(oRequiredRole.getRefInterface()));
        }

        return oReturnInterfaces;
    }


    /**
     * Get the event group by the given event group name.
     *
     * @param eventGroupName the given event group name.
     * @return the event group.
     */
    public EventGroupType getEventGroup(String eventGroupName) {
        String strXPath = m_namespaceDeclaration + "$this/tns:BBCoMInstance/tns:Depository/tns:EventGroup[@name='" + eventGroupName + "']";

        XmlObject[] oXmlObjects = m_oXmlObject.selectPath(strXPath);
        if (oXmlObjects == null || oXmlObjects.length == 0) {
            return null;
        }

        return ((EventGroupType) oXmlObjects[0]);
    }

    /**
     * Get the properties of the given device container.
     *
     * @param deviceContainer the given device container.
     * @return the properties.
     */
    public PropertiesType getProperties(DeviceContainerType deviceContainer) {
        return deviceContainer.getProperties();
    }

    /**
     * Get the scenario of the given deployment.
     *
     * @param deployment the given deployment.
     * @return the scenario.
     */
    public ScenarioType getScenario(DeploymentType deployment) {
        return deployment.getScenario();
    }

    /**
     * Get the list of event groups by the given deployable entity.
     *
     * @param deployableEntity the given deployable entity
     * @return the list of event groups.
     */
    public List<EventGroupType> getEventGroups(DeployableEntityType deployableEntity) {
        if (deployableEntity instanceof ComponentInstanceType) {
            return getEventGroups((ComponentInstanceType) deployableEntity);
        } else {
            EventGroupType oEventGroup = getEventGroup((EventChannelType) deployableEntity);

            List<EventGroupType> oReturnEventGroups = new ArrayList<>();
            oReturnEventGroups.add(oEventGroup);
            return oReturnEventGroups;
        }
    }

    /**
     * Get the event group by the given event channel.
     *
     * @param eventChannel the given event channel.
     * @return the list of implementations
     */
    public EventGroupType getEventGroup(EventChannelType eventChannel) {
        return getEventGroup(eventChannel.getRefEventGroup());
    }

    /**
     * Get the list of event groups by the given component instance.
     *
     * @param componentInstance the given component instance.
     * @return the list of event groups
     */
    public List<EventGroupType> getEventGroups(ComponentInstanceType componentInstance) {
        ComponentType oComponent = getComponent(componentInstance);
        if (oComponent instanceof PrimitiveComponentType) {
            return getEventGroups((PrimitiveComponentType) oComponent);
        } else {
            return null;
        }
    }

    /**
     * Get the list of event groups by the given primitive component.
     *
     * @param primitiveComponent the given primitive component.
     * @return the list of event groups.
     */
    private List<EventGroupType> getEventGroups(PrimitiveComponentType primitiveComponent) {
        List<EventGroupType> oReturnEventGroups = new ArrayList<>();
        for (SinkRoleType oSinkRole : primitiveComponent.getSinkRoleArray()) {
            addEventGroup(oReturnEventGroups, getEventGroup(oSinkRole.getRefEventGroup()));
        }
        for (SourceRoleType oSourceRoleType : primitiveComponent.getSourceRoleArray()) {
            addEventGroup(oReturnEventGroups, getEventGroup(oSourceRoleType.getRefEventGroup()));

        }

        return oReturnEventGroups;
    }


    /**
     * Get the list of primitive components of the given deployable entity.
     *
     * @param deployableEntity the given deployable entity.
     * @return the list of primitive components
     */
    public List<PrimitiveComponentType> getPrimitiveComponents(DeployableEntityType deployableEntity) {
        if (deployableEntity instanceof ComponentInstanceType) {
            return getPrimitiveComponents((ComponentInstanceType) deployableEntity);
        } else {
            return null;
        }
    }

    /**
     * Get the list of primitive components by the given component instance.
     *
     * @param componentInstance the given component instance.
     * @return the list of primitive components
     */
    public List<PrimitiveComponentType> getPrimitiveComponents(ComponentInstanceType componentInstance) {
        ComponentType oComponent = getComponent(componentInstance);
        if (oComponent instanceof PrimitiveComponentType) {
            List<PrimitiveComponentType> oReturnPrimitiveComponents = new ArrayList<>();
            oReturnPrimitiveComponents.add((PrimitiveComponentType) oComponent);
            return oReturnPrimitiveComponents;
        } else {
            return null;
        }
    }

    /**
     * Add the given primitive component to the given target list of  primitive components.
     *
     * @param targetListOfPrimitiveComponents the given target list of  primitive components.
     * @param primitiveComponent              the given  primitive component.
     */
    private void addPrimitiveComponent(List<PrimitiveComponentType> targetListOfPrimitiveComponents, PrimitiveComponentType primitiveComponent) {
        if (primitiveComponent != null && !targetListOfPrimitiveComponents.contains(primitiveComponent)) {
            targetListOfPrimitiveComponents.add(primitiveComponent);
        }
    }

    /**
     * Add the given primitive components to the given target list of primitive components.
     *
     * @param targetListOfPrimitiveComponents the given target list of primitive components.
     * @param primitiveComponents             the given primitive components.
     */
    private void addPrimitiveComponents(List<PrimitiveComponentType> targetListOfPrimitiveComponents, List<PrimitiveComponentType> primitiveComponents) {
        if (primitiveComponents == null) {
            return;
        }

        for (PrimitiveComponentType oPrimitiveComponent : primitiveComponents) {
            addPrimitiveComponent(targetListOfPrimitiveComponents, oPrimitiveComponent);
        }
    }


    /**
     * Get the device container of a deployable entity according the given deployment.
     *
     * @param deployment           the given deployment.
     * @param deployableEntityName the given deployable entity's name.
     * @return the device container.
     */
    public DeviceContainerType getDeviceContainerOfDeployableEntity(DeploymentType deployment, String deployableEntityName) {
        for (AllocationType oAllocation : deployment.getAllocationArray()) {
            if (deployableEntityName.equals(oAllocation.getRefDeployableEntity())) {
                DeviceEnvironmentType oDeviceEnvironment = getDeviceEnvironment(deployment);
                return getDeviceContainer(oDeviceEnvironment, oAllocation.getRefDeviceContainer());
            }
        }

        return null;
    }


    /**
     * Get the base folder name (for finding dependency files (*.jar, folders containing *.jar, *.class).
     *
     * @return the base folder name.
     */
    public String getBaseFolderName() {
        return new File(m_strBBCoMInstanceFilename).getAbsoluteFile().getParent();
    }

    /**
     * Set the quick validation.
     *
     * @param quickValidation the quick validation to set.
     */
    public void setQuickValidation(boolean quickValidation) {
        m_isQuickValidation = quickValidation;
    }

    /**
     * Get the filter rule (if any) of the given channel sink connector.
     *
     * @param channelSinkConnector the given channel sink connector.
     * @return the filter rule.
     */
    public FilterRuleType getFilterRule(ChannelSinkConnectorType channelSinkConnector) {
        return channelSinkConnector.getFilterRule();
    }


    /**
     * Get all the provided roles of the given system.
     *
     * @param system the given system.
     * @return all the provided roles.
     */
    public ProvidedRoleType[] getProvidedRoles(SystemType system) {
        return system.getProvidedRoleArray();
    }

    /**
     * Get all the sink roles of the given system.
     *
     * @param system the given system.
     * @return all the sink roles.
     */
    public SinkRoleType[] getSinkRoles(SystemType system) {
        return system.getSinkRoleArray();
    }

    /**
     * Get the user interface of a system-level role.
     * @param systemLevelRole the given system-level role.
     * @return the user interface.
     */
    public UserInterfaceType getUserInterface(AbstractProvidedRoleType systemLevelRole) {
        return systemLevelRole.getUserInterface();
    }
}